package com.yqg.protection.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.yqg.protection.common.Constant;
import com.yqg.protection.common.Result;
import com.yqg.protection.controller.dto.ImportUserDto;
import com.yqg.protection.controller.dto.UserDTO;
import com.yqg.protection.controller.dto.UserPasswordDTO;
import com.yqg.protection.entity.*;
import com.yqg.protection.entity.enums.OAIDEnum;
import com.yqg.protection.exception.ServiceException;
import com.yqg.protection.listenern.UserListener;
import com.yqg.protection.mapper.MenuMapper;
import com.yqg.protection.mapper.RoleMapper;
import com.yqg.protection.mapper.UserMapper;
import com.yqg.protection.service.IClientService;
import com.yqg.protection.service.IOrganizationService;
import com.yqg.protection.service.IUserService;
import com.yqg.protection.utils.Constants;
import com.yqg.protection.utils.JwtTokenUtil;
import com.yqg.protection.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * UserServiceImpl 服务实现类
 *
 * @author zxf
 * @since 2024-01-01
 */
@Service
@Slf4j
public class UserServiceImpl extends MPJBaseServiceImpl<UserMapper, User> implements IUserService {
    private final RedisUtil redisUtil;
    private final JwtTokenUtil jwtTokenUtil;
    @Resource
    private UserMapper userMapper;
    @Resource
    private RoleMapper roleMapper;

    @Autowired
    private IOrganizationService organizationService;

    @Autowired
    private MenuMapper menuMapper;

    @Resource
    private IClientService clientService;

    @Autowired
    private UserListener userListener;

    public UserServiceImpl(RedisUtil redisUtil, JwtTokenUtil jwtTokenUtil) {
        this.redisUtil = redisUtil;
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Override
    public UserDTO login(UserDTO userDTO) {
        // 用户密码 md5加密
        userDTO.setPassword(SecureUtil.md5(userDTO.getPassword()));
        User one = getUserInfo(userDTO);
        if (one != null) {
            if (!Objects.equals(one.getState(), "0")) {
                throw new ServiceException(Constant.CODE_402, "账户已锁定");
            }
            // 判断设备oaid
            // 首次绑定
            if (one.getOaid() == null ) {
                userDTO.setOaidState(OAIDEnum.FIRSTBIND.getValue());
                one.setOaid(userDTO.getOaid());
                this.updateById(one);
            }else {
                // 其他人尝试登录
                if (!Objects.equals(userDTO.getOaid(), one.getOaid())){
                    userDTO.setOaidState(OAIDEnum.OTHERBIND.getValue());
                }else {
                    // 正常登录
                    userDTO.setOaidState(OAIDEnum.NORMAL.getValue());
                }
            }
            //获取当前角色id
            Integer roleId = one.getRole();
            List<Menu> menus = menuMapper.selectMenus(roleId);
            userDTO.setMenus(menus);
            LambdaQueryWrapper<Organization> organizationQueryWrapper = new LambdaQueryWrapper<>();
            organizationQueryWrapper.eq(Organization::getId, one.getOrganization());
            organizationQueryWrapper.eq(Organization::getIsDelete, "0");
            Organization organization = organizationService.getOne(organizationQueryWrapper);
            BeanUtil.copyProperties(one, userDTO, true);
            userDTO.setOrganizationName(organization.getName());
            userDTO.setLevel(organization.getLevel());
            if (userDTO.getIsAllClient().equals("1")) {
                List<Long> clientIds = new ArrayList<>();
                clientIds.addAll(userDTO.getClientIds());
                while (clientIds.size() > 0) {
                    LambdaQueryWrapper<Client> clientLambdaQueryWrapper = new LambdaQueryWrapper<>();
                    clientLambdaQueryWrapper.in(Client::getPcId, clientIds);
                    clientIds = clientService.list(clientLambdaQueryWrapper).stream().map(Client::getId).collect(Collectors.toList());
                    userDTO.getClientIds().addAll(clientIds);
                }
            }
            Role role = roleMapper.selectById(roleId);
            userDTO.setRoleName(role.getRolename());
            // 设置token
            String token = jwtTokenUtil.generateToken(one);
            //如果redis已经有了这个token ，就不添加
            if (!redisUtil.hasKey(token)) {
                redisUtil.set(token, one, jwtTokenUtil.getExpiration());
                //给token重设时间
                redisUtil.expire(token, Constants.EXPIRATION_TIME);
            }
            userDTO.setToken(token);
            return userDTO;
        } else {
            throw new ServiceException(Constant.CODE_600, "用户名或密码错误");
        }
    }

    @Override
    public User register(UserDTO userDTO) {
        // 用户密码 md5加密
        userDTO.setPassword(SecureUtil.md5(userDTO.getPassword()));
        User one = getUserInfo(userDTO);
        if (one == null) {
            one = new User();
            BeanUtil.copyProperties(userDTO, one, true);
            // 默认一个普通用户的角色
            if (one.getUsername() == null) {
                one.setUsername(one.getUsername());
            }
            save(one);  // 把 copy完之后的用户对象存储到数据库
        } else {
            throw new ServiceException(Constant.CODE_600, "用户已存在");
        }
        return one;
    }

    @Override
    public void updatePassword(UserPasswordDTO userPasswordDTO) {
        int update = userMapper.updatePassword(userPasswordDTO);
        if (update < 1) {
            throw new ServiceException(Constant.CODE_600, "密码错误");
        }
    }

    /**
     * 重置密码(123456)
     */
    @Override
    public int resetPwd(int id, String password, Date date) {
        return userMapper.resetPwd(id, password, date);
    }

    /**
     * 从数据库查询用户信息
     *
     * @param userDTO userDTO
     * @return User
     */
    private User getUserInfo(UserDTO userDTO) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", userDTO.getUsername());
        queryWrapper.eq("password", userDTO.getPassword());
        User one;
        try {
            one = userMapper.getLoginUser(userDTO.getUsername(), userDTO.getPassword()); // 从数据库查询用户信息
        } catch (Exception e) {
            log.error(String.valueOf(e));
            throw new ServiceException(Constant.CODE_500, "系统错误");
        }
        return one;
    }

    @Override
    public void export(HttpServletResponse response, Integer level, Long organizationId, Long userId, String keyWord, Integer role) {
        try {
            //设置下载
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
            String fileName = null;
            fileName = URLEncoder.encode("用户信息表", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");

            //查询用户所有数据
            List<User> exportData = userMapper.exportData(level,organizationId,userId,keyWord,role);
            //EasyExcel 写操作
            EasyExcel.write(response.getOutputStream(), User.class)
                    .sheet("用户信息表")
                    .doWrite(exportData);
        } catch (Exception e) {
            log.info("exception ", e);
        }
    }

    @Override
    public User getUserById(Long userId) {
        return userMapper.getUserById(userId);
    }

    @Override
    public List<User> listUsersByIds(Collection<Long> userIds) {
        return userMapper.listUsersByIds(userIds);
    }

    @Override
    public Object importData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), ImportUserDto.class, userListener)
                    .sheet(0).headRowNumber(1).doRead();
            return Result.success();
        } catch (Exception e) {
            log.info("exception ", e);
            return Result.error("导入异常，请稍后重试！");
        }
    }
}
